using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace InfinateSki
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        //Graphics
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        public Texture2D snow, man, title, menuBackground;
        private SpriteFont font, menuFont;
        private float scale;


        //These are for the menu is the menu

        Menu activeMenu, menuMain, newGame, optionsMenu1;
        enum GameState { titleScreen, menuScreen, gameScreen}
        GameState state = GameState.titleScreen;

        private KeyboardState lastState;

        //man
        // rotation in radians is opposite to the radian conversion table, 0 is 0 but 4.7 is 1.56 etc.
        private float rotation;
        private float manWidth;
        private float manHeight;
        private float manSpeed;
        private float manPosX;
        private float manPosY;
        private float manAccel;
        

        //background
        //two lists, one for the background images on either side and one for the back ground images
        // going down. The wide list produces background lists starting from 2 bgWidths away.
        private List<Background> bgLong;
        private List<List<Background>> bgWide;

        private int longSize;
        private int wideSize;


        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            


            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            
            snow = Content.Load<Texture2D>("Background/Snow");
            man = Content.Load<Texture2D>("Man/Man");
            font = Content.Load<SpriteFont>("SpriteFont1");
            title = Content.Load<Texture2D>("Background/TitleScreen");
            menuBackground = Content.Load<Texture2D>("Background/menuBackground");
            
            // the scale

            scale = 0.9f;

            // The menu

            menuFont = Content.Load<SpriteFont>("Fonts/menuFont");
            
            menuMain = new Menu(menuFont, "Main Menu", menuBackground);
            optionsMenu1 = new Menu(menuFont, "Options", menuBackground);
            newGame = new Menu(null, null, null);
    
            
            menuMain.AddMenuItem("New Game", newGame);
            menuMain.AddMenuItem("Options", optionsMenu1);
            menuMain.AddMenuItem("Exit Game", null);
            activeMenu = menuMain;

            optionsMenu1.AddMenuItem("Option 1", menuMain);
            optionsMenu1.AddMenuItem("Option 2", null);
            optionsMenu1.AddMenuItem("Option 3", null);




            // the background list
            wideSize = 4;
            longSize = 5;
            float startPos = 0 - (snow.Width *scale) * 2;

            bgWide = new List<List<Background>>();

            while (bgWide.Count <= wideSize)
            {



                bgLong = new List<Background>();
                bgLong.Add(new Background(startPos, 0));

                while (bgLong.Count <= longSize)
                {
                    bgLong.Add(new Background(startPos,
                    bgLong.ElementAt(bgLong.Count - 1).yPos + (snow.Height*scale)));
                }
                bgWide.Add(bgLong);
                startPos += snow.Width * scale;

            }



            // TODO: use this.Content to load your game content here

            //man

            rotation = 0;
            manWidth = man.Width;
            manHeight = man.Height;

            manSpeed = 0.5f;
            manPosX = 200;
            manPosY = 200;
            manAccel = 1;
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // TODO: Add your update logic here

             KeyboardState keyboard = Keyboard.GetState();


             switch (state)
             {
                 case GameState.gameScreen:


                     if (keyboard.IsKeyDown(Keys.Left))
                     {
                     }

                     if (keyboard.IsKeyDown(Keys.Left))
                     {
                         rotation += -0.1f;
                     }

                     if (keyboard.IsKeyDown(Keys.Right))
                     {
                         rotation += 0.1f;
                     }


                     //keep rotation within 0 to 2 pie radians;

                     if (rotation < 0)
                     {
                         rotation = 2*(float) Math.PI;

                     }
                     if (rotation > 2*(float) Math.PI)
                     {
                         rotation = 0;

                     }

                     manAccel = accelFinder((double) rotation);
                     if (manSpeed + manAccel < 2 && manSpeed + manAccel > -2)
                     {
                         manSpeed += manAccel;
                     }


                     // move the backgrounds in the lists
                     foreach (List<Background> x in bgWide)
                     {
                         foreach (Background y in x)
                         {
                             y.xPos -= (float) Math.Cos((double) rotation)*manSpeed;
                             y.yPos -= (float) Math.Sin((double) rotation)*manSpeed;
                         }

                     }
                     break;
                 case GameState.titleScreen:
                     if (keyboard.IsKeyDown(Keys.Enter))
                     {
                         state = GameState.menuScreen;
                     }
                     break;

                 case GameState.menuScreen:
                     MenuInput(keyboard);

                     break;
             }

            //Update the background when the man has crossed over them

            if(bgWide.ElementAt(0).ElementAt(0).yPos < (0 - ((snow.Height*scale) * 3)))
            {
                for (int i = 0; i <= bgWide.Count - 1;i++ )
                {
                    bgWide.ElementAt(i).Add(new Background(bgWide.ElementAt(i).ElementAt(0).xPos, bgWide.ElementAt(i).Last().yPos + (snow.Height*scale)));
                    bgWide.ElementAt(i).RemoveAt(0);

                }

            }

            // extends the left side
            // trims it down
            float startXPos = 0;
            float startYPos = 0 - snow.Height * scale;
            if (bgWide.ElementAt(0).ElementAt(0).xPos > 0)
            {
                bgLong = new List<Background>();
                while (bgLong.Count <= longSize)
                {
                    bgLong.Add(new Background(startXPos - (snow.Width*scale),startYPos));
                    startYPos += snow.Height*scale;
                }

                bgWide.Insert(0, bgLong);
                bgWide.RemoveAt(bgWide.Count - 1);


            }

            // extends the right side
            //trims it down

             startXPos = (int) bgWide.Last().Last().xPos;
             startYPos = 0 - snow.Height * scale;

            if (bgWide.Last().ElementAt(0).xPos < (snow.Width*scale) )
            {
                bgLong = new List<Background>();
                while (bgLong.Count <= longSize)
                {
                    bgLong.Add(new Background(startXPos + (snow.Width * scale), startYPos));
                    startYPos += (snow.Height*scale);
                }

                bgWide.Insert(bgWide.Count, bgLong);
                bgWide.RemoveAt(0);
                //bgWide.RemoveAt(0);

            }



    //        manPosX += (float) Math.Cos((double)rotation)*manSpeed;
    //        manPosY += (float)Math.Sin((double)rotation) * manSpeed;



            lastState = keyboard;
            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        /// 

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            spriteBatch.Begin();
            switch (state)
            {
                case GameState.gameScreen:

                    foreach (List<Background> x in bgWide)
                    {
                        foreach (Background y in x)
                        {
                            spriteBatch.Draw(snow, new Vector2(y.xPos, y.yPos), null, Color.White, 0, new Vector2(0, 0),
                                             new Vector2(scale, scale), SpriteEffects.None, 0);
                        }

                    }

                    //spriteBatch.Draw(snow, new Vector2(0,0),null,Color.White,0,new Vector2(0,0),new Vector2(1,1),SpriteEffects.None,0);
                    spriteBatch.Draw(man, new Vector2(manPosX, manPosY), null, Color.White, rotation,
                                     new Vector2(manWidth/2, manHeight/2), new Vector2(scale, scale), SpriteEffects.None,
                                     0.1f);
                    spriteBatch.DrawString(font, "" + rotation + "", new Vector2(100, 100), Color.Violet);


                    /*
                     * spriteBatch.Draw(background, new Vector2(backgroundX,backgroundY), null, Color.White, 0, new Vector2(0, 0), new Vector2(1, 1), SpriteEffects.None, 0.5f);
                       spriteBatch.Draw(bot, new Vector2(botX, botY), null, Color.White, 0, new Vector2(0, 0), new Vector2(1, 1), SpriteEffects.None, 0.5f);
                     * 
                     */

                    // TODO: Add your drawing code here);

                    base.Draw(gameTime);
                    break;

                case GameState.titleScreen:

                    spriteBatch.Draw(title, new Vector2(0, 0), null, Color.White, 0, new Vector2(0, 0),
                                     new Vector2(1, 1), SpriteEffects.None, 0);

                    break;
                case GameState.menuScreen:
                    activeMenu.Draw(spriteBatch);
                    break;
            }
            spriteBatch.End();
        }

        // My Functions

        // accelFinder has to take a double because this uses modulas which will not accept a float for some reason
        // feed it the rotation and it will give you the speed
        private float accelFinder(double rot)
        {
            double after = 0;
            double num = 0;
            if(rot == 0)
            {
                rot += 0.001;
            }
            num = (1.7) - Math.Abs(1.7 - rot);
            
            
            return (float) num * 0.5f;
        }


        private void MenuInput(KeyboardState curState)
        {

            Menu newMenu = activeMenu.ProcessInput(lastState, curState);

            if (newMenu == null)
                this.Exit();
            if (newMenu == newGame)
                state = GameState.gameScreen;

            activeMenu = newMenu;
            //activeMenu.Draw(spriteBatch);
            lastState = curState;
        }

    }
}
